#include "cpu.h"
#include "fifo.h"
#include "work.h"
#include "usart_driver.h"



struct COM_DMA_S
{
	USART_TypeDef * USARTx;
	u32 rcc_perihp;

	GPIO_TypeDef *GPIO_tx;
	u16 pin_tx;
	GPIO_TypeDef *GPIO_rx;
	u16 pin_rx;

	DMA_Channel_TypeDef* DMA_Txchannel;
	DMA_Channel_TypeDef* DMA_Rxchannel;
};


#define COM1_RXTX_BUFF_SIZE 4096
static u8 com1_tx_buff[COM1_RXTX_BUFF_SIZE]={0};
static u8 com1_rx_buff[COM1_RXTX_BUFF_SIZE]={0};

#define COM2_RXTX_BUFF_SIZE 10
static u8 com2_tx_buff[COM2_RXTX_BUFF_SIZE]={0};
static u8 com2_rx_buff[COM2_RXTX_BUFF_SIZE]={0};

#define COM3_RXTX_BUFF_SIZE 10
static u8 com3_tx_buff[COM3_RXTX_BUFF_SIZE]={0};
static u8 com3_rx_buff[COM3_RXTX_BUFF_SIZE]={0};

#define COM4_RXTX_BUFF_SIZE 10
static u8 com4_tx_buff[COM4_RXTX_BUFF_SIZE]={0};
static u8 com4_rx_buff[COM4_RXTX_BUFF_SIZE]={0};

#define COM5_RXTX_BUFF_SIZE 10
static u8 com5_tx_buff[COM5_RXTX_BUFF_SIZE]={0};
static u8 com5_rx_buff[COM5_RXTX_BUFF_SIZE]={0};

#define COM6_RXTX_BUFF_SIZE 10
static u8 com6_tx_buff[COM6_RXTX_BUFF_SIZE]={0};
static u8 com6_rx_buff[COM6_RXTX_BUFF_SIZE]={0};

#define COM7_RXTX_BUFF_SIZE 10
static u8 com7_tx_buff[COM7_RXTX_BUFF_SIZE]={0};
static u8 com7_rx_buff[COM7_RXTX_BUFF_SIZE]={0};

#define COM8_RXTX_BUFF_SIZE 10
static u8 com8_tx_buff[COM8_RXTX_BUFF_SIZE]={0};
static u8 com8_rx_buff[COM8_RXTX_BUFF_SIZE]={0};



static struct FIFO_S com_fifo_tx[COM_MAX]={
	{.head=0, .tail=0, .size=COM1_RXTX_BUFF_SIZE, .buff=com1_tx_buff},\
	{.head=0, .tail=0, .size=COM2_RXTX_BUFF_SIZE, .buff=com2_tx_buff},\
	{.head=0, .tail=0, .size=COM3_RXTX_BUFF_SIZE, .buff=com3_tx_buff},\
	{.head=0, .tail=0, .size=COM4_RXTX_BUFF_SIZE, .buff=com4_tx_buff},\
	{.head=0, .tail=0, .size=COM5_RXTX_BUFF_SIZE, .buff=com5_tx_buff},\
	{.head=0, .tail=0, .size=COM6_RXTX_BUFF_SIZE, .buff=com6_tx_buff},\
	{.head=0, .tail=0, .size=COM7_RXTX_BUFF_SIZE, .buff=com7_tx_buff},\
	{.head=0, .tail=0, .size=COM8_RXTX_BUFF_SIZE, .buff=com8_tx_buff},\
};

static struct FIFO_S com_fifo_rx[COM_MAX]={
	{.head=0, .tail=0, .size=COM1_RXTX_BUFF_SIZE, .buff=com1_rx_buff},\
	{.head=0, .tail=0, .size=COM2_RXTX_BUFF_SIZE, .buff=com2_rx_buff},\
	{.head=0, .tail=0, .size=COM3_RXTX_BUFF_SIZE, .buff=com3_rx_buff},\
	{.head=0, .tail=0, .size=COM4_RXTX_BUFF_SIZE, .buff=com4_rx_buff},\
	{.head=0, .tail=0, .size=COM5_RXTX_BUFF_SIZE, .buff=com5_rx_buff},\
	{.head=0, .tail=0, .size=COM6_RXTX_BUFF_SIZE, .buff=com6_rx_buff},\
	{.head=0, .tail=0, .size=COM7_RXTX_BUFF_SIZE, .buff=com7_rx_buff},\
	{.head=0, .tail=0, .size=COM8_RXTX_BUFF_SIZE, .buff=com8_rx_buff},\
};

const static struct COM_DMA_S comx[COM_MAX]={
	{USART1, RCC_APB2Periph_USART1, GPIOA,GPIO_Pin_9,  GPIOA,GPIO_Pin_10, DMA1_Channel4, DMA1_Channel5},
	{USART2, RCC_APB1Periph_USART2, GPIOA,GPIO_Pin_2,  GPIOA,GPIO_Pin_3 , DMA1_Channel7, DMA1_Channel8},
	{USART3, RCC_APB1Periph_USART3, GPIOB,GPIO_Pin_10, GPIOB,GPIO_Pin_11, DMA1_Channel2, DMA1_Channel3},
	{UART4,  RCC_APB1Periph_UART4,  GPIOC,GPIO_Pin_10, GPIOC,GPIO_Pin_11, DMA2_Channel5, DMA2_Channel3},
	{UART5,  RCC_APB1Periph_UART5,  GPIOC,GPIO_Pin_12, GPIOD,GPIO_Pin_2 , DMA2_Channel4, DMA2_Channel2},
	{UART6,  RCC_APB1Periph_UART6,  GPIOB,GPIO_Pin_8 , GPIOB,GPIO_Pin_9 , DMA2_Channel6, DMA2_Channel7},
	{UART7,  RCC_APB1Periph_UART7,  GPIOC,GPIO_Pin_2 , GPIOC,GPIO_Pin_3 , DMA2_Channel8, DMA2_Channel9},
	{UART8,  RCC_APB1Periph_UART8,  GPIOC,GPIO_Pin_4 , GPIOC,GPIO_Pin_5 , DMA2_Channel10,DMA2_Channel11},
};

static void com_dma_data_check(E_COM index);

u8 com_dma_usart_init(E_COM index,u32 BaudRate,u8 optional)
{
	const struct COM_DMA_S * pcom;
	struct FIFO_S * Q;
	USART_TypeDef* USARTx;
	USART_InitTypeDef  USART_InitStructure;
	DMA_InitTypeDef DMA_InitStruct;
	GPIO_InitTypeDef  GPIO_InitStructure = {0};

	if((index>= COM_MAX) ||(BaudRate==0))
		return 0;

	pcom = &comx[index];

	switch(index)
	{
		case COM_USART1:
			RCC_APB2PeriphClockCmd( pcom->rcc_perihp, ENABLE );
		break;
		case COM_UART6:
            RCC_APB1PeriphClockCmd( pcom->rcc_perihp, ENABLE );
			GPIO_PinRemapConfig( GPIO_PartialRemap_USART6 , ENABLE );

		break;
		default:
			RCC_APB1PeriphClockCmd( pcom->rcc_perihp, ENABLE );
		break;
	}

	GPIO_InitStructure.GPIO_Pin = pcom->pin_tx;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init( pcom->GPIO_tx, &GPIO_InitStructure );

    GPIO_InitStructure.GPIO_Pin = pcom->pin_rx;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init( pcom->GPIO_rx, &GPIO_InitStructure );

	USARTx = pcom->USARTx;

	USART_InitStructure.USART_BaudRate = BaudRate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	USART_Init(USARTx, &USART_InitStructure);

	USART_ClearFlag(USARTx,USART_FLAG_TC);
	USART_ITConfig(USARTx, USART_IT_TXE, DISABLE);

	/**************************usart DMA set******************************/
    switch(index)
	{
		case COM_USART1 ... COM_USART3:
			RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE);
		break;
		case COM_UART4 ... COM_UART8:
			RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA2, ENABLE);
		break;
		default:break;
	}

	/**************************receive config******************************/
	Q=&com_fifo_rx[index];
	DMA_DeInit(pcom->DMA_Rxchannel);
	DMA_InitStruct.DMA_PeripheralBaseAddr	= (u32)&(pcom->USARTx->DATAR);
	DMA_InitStruct.DMA_MemoryBaseAddr		= (u32)Q->buff;
	DMA_InitStruct.DMA_DIR					= DMA_DIR_PeripheralSRC;
	DMA_InitStruct.DMA_BufferSize			= Q->size;
	DMA_InitStruct.DMA_PeripheralInc		= DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_MemoryInc			= DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralDataSize	= DMA_PeripheralDataSize_Byte;
	DMA_InitStruct.DMA_MemoryDataSize		= DMA_MemoryDataSize_Byte;
	DMA_InitStruct.DMA_Mode 				= DMA_Mode_Circular;
	DMA_InitStruct.DMA_Priority 			= DMA_Priority_VeryHigh;
	DMA_InitStruct.DMA_M2M					= DMA_M2M_Disable;

	DMA_Init(pcom->DMA_Rxchannel, &DMA_InitStruct);
	DMA_Cmd(pcom->DMA_Rxchannel, ENABLE);
	USART_DMACmd(USARTx,USART_DMAReq_Rx,ENABLE);

	/*************************transmit config*******************************/
	Q=&com_fifo_tx[index];
	DMA_DeInit(pcom->DMA_Txchannel);
	DMA_InitStruct.DMA_PeripheralBaseAddr	= (u32)&(pcom->USARTx->DATAR);
	DMA_InitStruct.DMA_MemoryBaseAddr 		= (u32)(u32)Q->buff;
	DMA_InitStruct.DMA_DIR 					= DMA_DIR_PeripheralDST;
	DMA_InitStruct.DMA_BufferSize			= 0;
	DMA_InitStruct.DMA_PeripheralInc 		= DMA_PeripheralInc_Disable;
	DMA_InitStruct.DMA_MemoryInc 			= DMA_MemoryInc_Enable;
	DMA_InitStruct.DMA_PeripheralDataSize	= DMA_PeripheralDataSize_Byte;
	DMA_InitStruct.DMA_MemoryDataSize 		= DMA_MemoryDataSize_Byte;
	DMA_InitStruct.DMA_Mode 				= DMA_Mode_Normal;
	DMA_InitStruct.DMA_Priority 			= DMA_Priority_Medium;
	DMA_InitStruct.DMA_M2M 					= DMA_M2M_Disable;

	DMA_Init(pcom->DMA_Txchannel, &DMA_InitStruct);
	DMA_Cmd(pcom->DMA_Txchannel, ENABLE);
	USART_DMACmd(USARTx,USART_DMAReq_Tx,ENABLE);

	USART_Cmd(USARTx, ENABLE);
    work_add_tail((void (*)(unsigned int data))com_dma_data_check,index);

	return 1;
}

void com_dma_usart_uninit(E_COM index)
{
	const struct COM_DMA_S * pcom;

	if(index>= COM_MAX)
		return ;

	USART_DMACmd(pcom->USARTx,USART_DMAReq_Rx,DISABLE);
	USART_DMACmd(pcom->USARTx,USART_DMAReq_Tx,DISABLE);
	USART_Cmd(pcom->USARTx, ENABLE);
}

static void com_dma_send_check(E_COM index)
{
	u32 len;
	const struct COM_DMA_S * pcom;
	struct FIFO_S * Q;

	if(index>= COM_MAX)
		return ;

	Q=&com_fifo_tx[index];
	if(fifo_is_empty(Q))
		return ;

	pcom = &comx[index];

	if(DMA_GetCurrDataCounter(pcom->DMA_Txchannel) == 0)//上一次发送完成
	{
		len = fifo_get_length_continue(Q);//需要连续的一块长度

		DMA_Cmd(pcom->DMA_Txchannel,  DISABLE);
		DMA_SetCurrDataCounter(pcom->DMA_Txchannel,len);
		pcom->DMA_Txchannel->MADDR = (u32)fifo_get_head_addr(Q);
		DMA_Cmd(pcom->DMA_Txchannel,  ENABLE);
		fifo_head_offset( Q, len);
	}
}
u16 com_dma_send_data(E_COM index,const u8 * buff, u16 size)
{
	struct FIFO_S * Q;

	if(index>=COM_MAX)
		return 0;

	Q=&com_fifo_tx[index];
	size = fifo_set_data(Q,buff,size);

    return size;
}
static void com_dma_receive_check(E_COM index)
{
	const struct COM_DMA_S * pcom;
	struct FIFO_S * Q;
	u32 tail;

	if(index>= COM_MAX)
		return ;

	Q=&com_fifo_rx[index];
	pcom = &comx[index];

	tail = fifo_get_size(Q) - DMA_GetCurrDataCounter(pcom->DMA_Rxchannel);

	fifo_tail_set(Q,tail);
}

struct FIFO_S * com_dma_rx_fifo_get(E_COM index)
{
	if(index>=COM_MAX)
		return 0;
	return &com_fifo_rx[index];
}

struct FIFO_S * com_dma_tx_fifo_get(E_COM index)
{
	if(index>=COM_MAX)
		return 0;
	return &com_fifo_tx[index];
}


static void com_dma_data_check(E_COM index)
{
	com_dma_send_check(index);
	com_dma_receive_check(index);
}


